/**
 *  Copyright 2010 Kyle O'Connor, Justin Arling, Mike Mercieca
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package edu.drexel.sd0910.ece01.aqmon.gui;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Properties;
import java.util.Vector;

import org.apache.log4j.Logger;

import com.rapplogic.xbee.api.XBeeAddress64;

import edu.drexel.sd0910.ece01.aqmon.data.datastore.DataStore;
import edu.drexel.sd0910.ece01.aqmon.manager.EmailNotificationManager;
import edu.drexel.sd0910.ece01.aqmon.manager.FileOutputManager;
import edu.drexel.sd0910.ece01.aqmon.manager.FileUploadManager;
import edu.drexel.sd0910.ece01.aqmon.manager.Manager;
import edu.drexel.sd0910.ece01.aqmon.manager.WeatherDataManager;
import edu.drexel.sd0910.ece01.aqmon.manager.xbee.XBeeManager;
import edu.drexel.sd0910.ece01.aqmon.util.TimeUtils;
import gnu.io.CommPortIdentifier;

/**
 * Main GUI model - Model component (MVC pattern)<br>
 * This class represents all the data that needs to be kept track of and is the
 * main point for access to all active managers. This is also an
 * <code>{@link Observable}</code> type, for signaling updates to the view.
 * 
 * @author Kyle O'Connor
 * 
 */
public class MainModel extends Observable {

	private final static Logger log = Logger.getLogger(MainModel.class);

	protected Properties props;
	protected DataStore dataStore;
	protected Map<String, CommPortIdentifier> portMap;
	protected Map<XBeeAddress64, String> addressMap;
	protected String status;
	protected List<String> terminalData;
	protected String portName;
	protected volatile boolean captureEnabled; //thread-safe flag
	protected String resolvedLocation;
	protected String deploymentDirectory;
	
	protected ArrayList<Manager> activeManagers;
	protected FileOutputManager outputFileManager;
	protected FileUploadManager uploadFileManager;
	protected WeatherDataManager weatherDataManager;
	protected EmailNotificationManager emailNotificationManager;
	protected XBeeManager xbeeManager = null;

	public MainModel(String configFile) {
		props = loadProperties(configFile);
		dataStore = new DataStore(this.props);
		activeManagers = new ArrayList<Manager>();

		captureEnabled = false;
		terminalData = new ArrayList<String>();
		status = "Air Quality Monitoring system ready...";
		xbeeManager = new XBeeManager(this, this.props);
		addActiveManager(xbeeManager); //keep track of this manager
		portMap = xbeeManager.populatePorts();
		addressMap = xbeeManager.populateAddresses();
		
		// set up file uploading
		uploadFileManager = new FileUploadManager(this, this.props);
		addActiveManager(uploadFileManager); // keep track of this manager
		
		weatherDataManager = new WeatherDataManager(this, this.props);
		addActiveManager(weatherDataManager);
		
		emailNotificationManager = new EmailNotificationManager(this, this.props);
		addActiveManager(emailNotificationManager);
		
		// set up CSV outputting
		try {
			outputFileManager = new FileOutputManager(this, this.props);
			addActiveManager(outputFileManager); //keep track of this manager
		} catch (IOException e) {
			log.fatal(e.toString());
			System.exit(1);
		}
	}

	public String getPortNameFromList(String name) {
		return portMap.get(name).getName();
	}
	
	public Vector<String> getPortList() {
		return new Vector<String>(portMap.keySet());
	}

	public String getPortName() {
		return portName;
	}

	public void setPortName(String portName) {
		this.portName = portName;
	}
	
	public String getNodeIDFromAddress(XBeeAddress64 nodeAddress) {
		return addressMap.get(nodeAddress);
	}
	
	public Map<XBeeAddress64, String> getAddressMap() {
		return addressMap;
	}
	
	public List<String> getTerminalData() {
		return terminalData;
	}

	public void appendToTerminal(String line) {
		terminalData.add(line);
		this.setChanged();
		this.notifyObservers(GUIUpdates.TERMINAL);
	}
	
	public String getStatusText() {
		return status;
	}

	public void setStatusText(String s) {
		this.status = s;
		this.setChanged();
		this.notifyObservers(GUIUpdates.STATUS_BAR);
	}
	
	public ArrayList<Manager> getActiveManagers() {
		return activeManagers;
	}
	
	private void addActiveManager(Manager manager) {
		activeManagers.add(manager);
	}
	
	public XBeeManager getXbeeManager() {
		return xbeeManager;
	}
	
	public EmailNotificationManager getEmailNotificationManager() {
		return emailNotificationManager;
	}
	
	public synchronized FileOutputManager getFileOutputManager() {
		return outputFileManager;
	}

	public synchronized FileUploadManager getFileUploadManager() {
		return uploadFileManager;
	}

	public synchronized WeatherDataManager getWeatherDataManager() {
		return weatherDataManager;
	}

	public synchronized DataStore getDataStore() {
		return dataStore;
	}
	
	public boolean isCaptureEnabled() {
		return captureEnabled;
	}

	public void setCaptureEnabled(boolean captureEnabled) {
		this.captureEnabled = captureEnabled;
	}
	
	private Properties loadProperties(String filename) {
		Properties properties = new Properties();
		try {
			FileInputStream fis = new FileInputStream(filename);
			properties.loadFromXML(fis);
		} catch (Exception e) {
			log.fatal(e);
			System.exit(1);
		}
		return properties;
	}

	public Properties getProperties() {
		return props;
	}

	public void setProperties(Properties prop) {
		this.props = prop;
	}

	/**
	 * Should never be <code>null</code>.
	 * 
	 * @return resolved location (zipcode) as <code>String</code>
	 */
	public String getResolvedLocation() {
		return resolvedLocation;
	}

	public void setResolvedLocation(String resolvedLocation) {
		log.debug("Resolved location set to: "+resolvedLocation);
		this.resolvedLocation = resolvedLocation;
	}

	public long getTimezoneOffset() {
		return TimeUtils.calculateTimeZoneOffset();
	}

	public String getDeploymentDirectory() {
		return deploymentDirectory;
	}

	public void setDeploymentDirectory(String deploymentDirectory) {
		this.deploymentDirectory = deploymentDirectory;
	}

}
